package general

import (
	"bufio"
	"context"
	"fmt"
	"gitee.com/kaylee595/gkaylee/kerr"
	proxyPool "gitee.com/kaylee595/proxy-pool"
	"github.com/imroc/req/v3"
	"net/url"
	"strings"
	"time"
)

type source struct {
	url            string
	httpClient     *req.Client
	splitFunc      bufio.SplitFunc
	defaultScheme  string
	defaultAuth    *url.Userinfo
	expireDuration time.Duration
	maxUses        uint64
	urlParser      proxyPool.URLParser
}

// New
// 默认采用行分割的形式读取ip, 可以为\n或\r\n, 修改请用 WithSplitFunc;
// 每一行的代理解析采用 proxyPool.PlatformStdURLParser, 修改请用 WithURLParser;
// 如果获取到的代理没有Scheme, 则默认使用http, 修改请用 WithScheme;
// 获取到的代理如果没有包含授权信息, 但是需要有默认的授权信息请使用 WithAuth;
// 每个代理的有效期被标记为五分钟, 修改请用 WithExpire;
// 每个只要没过期, 就可以一直被使用, 如果需要设定最大使用次数请使用 WithMaxUses;
func New(url string, opts ...Option) proxyPool.Source {
	g := &source{
		url:            url,
		httpClient:     req.DefaultClient(),
		splitFunc:      bufio.ScanLines,
		defaultScheme:  "http",
		expireDuration: 5 * time.Minute,
		urlParser:      proxyPool.StdURLParser,
	}
	for _, opt := range opts {
		opt(g)
	}
	return g
}

func (s *source) Fetch(ctx context.Context) ([]proxyPool.Node, error) {
	resp := s.httpClient.Get(s.url).Do(ctx)
	if resp.Err != nil {
		return nil, kerr.WrapTraceStack(resp.Err)
	}
	if !resp.IsSuccessState() {
		return nil, kerr.WrapTraceStack(fmt.Errorf("status code: %d, resp body: %s", resp.StatusCode, resp.String()))
	}
	scanner := bufio.NewScanner(resp.Body)
	scanner.Split(s.splitFunc)
	rets := make([]proxyPool.Node, 0)
	for scanner.Scan() {
		row := strings.TrimSpace(scanner.Text())
		if row == "" {
			continue
		}
		proxyUrl, err := s.urlParser.Parse(row)
		if err != nil {
			return nil, kerr.WrapTraceStack(fmt.Errorf("%w, rawUrl: %s", err, row))
		}
		if proxyUrl.Scheme == "" {
			proxyUrl.Scheme = s.defaultScheme
		}
		if proxyUrl.User == nil {
			proxyUrl.User = s.defaultAuth
		}
		if s.maxUses > 0 && s.expireDuration > 0 {
			rets = append(rets, proxyPool.NewNodeWithExpirationDurationMaxUses(proxyUrl, s.expireDuration, s.maxUses))
		} else if s.expireDuration > 0 {
			rets = append(rets, proxyPool.NewNodeWithExpirationDuration(proxyUrl, s.expireDuration))
		} else if s.maxUses > 0 {
			rets = append(rets, proxyPool.NewNodeWithMaxUses(proxyUrl, s.maxUses))
		}
	}
	if scanner.Err() != nil {
		return nil, kerr.WrapTraceStack(scanner.Err())
	}
	return rets, nil
}
